﻿using System;
using System.Collections.Generic;
using System.Collections;
using System.Text;
using System.Data;
using System.Xml;
using System.IO;
using System.Timers;
using System.Reflection;

using System.Data.SqlClient;
using System.Data.OleDb;
using System.Data.OracleClient;
using System.Data.Odbc;

namespace Srvtools
{
    public static class DbConnectionSet
    {
        //private static ArrayList m_dbConnections = new ArrayList();

        private static Hashtable DbConnections = new Hashtable();

        //private static ArrayList DbConnections
        //{
        //    get
        //    {
        //        return m_dbConnections;
        //    }
        //}

        //USE FOR DESIGN!!!
        internal static string[] GetAvaliableAlias()
        { 
            string s = SystemFile.DBFile;
            
            if (File.Exists(s))
            {
                XmlDocument xml = new XmlDocument();
                xml.Load(s);
                XmlNode node = xml.SelectSingleNode("InfolightDB/DataBase");
                if (node != null)
                {
                    List<string> listAlias = new List<string>();
                    foreach (XmlNode nodeChild in node.ChildNodes)
                    {
                        listAlias.Add(nodeChild.Name);
                    }
                    return listAlias.ToArray();
                }
            }
            return new string[] { };
        }

        private static string GetPwdString(string s)
        {
            string sRet = "";
            for (int i = 0; i < s.Length; i++)
            {
                sRet = sRet + (char)(((int)(s[s.Length - 1 - i])) ^ s.Length);
            }
            return sRet;
        }

        internal static IDbConnection WaitForConnection(string dbName, ref ClientType ct, string user, string module, DateTime time)
        {
            DbConnection dbConn = GetDbConn(dbName);

            if (dbConn != null)
            {
                ct = dbConn.DbType;
                return dbConn.WaitOne(user, module, time);
            }
            else
            {
                return null;
            }
        }

        internal static void ReleaseConnection(string dbName, IDbConnection conn)
        {
            DbConnection dbConn = GetDbConn(dbName);

            if (dbConn != null)
            {
                dbConn.Release(conn);
            }
        }

        public static DbConnection GetDbConn(string dataBaseName)
        {
            lock (typeof(DbConnectionSet))
            {
                if (!DbConnections.Contains(dataBaseName))
                {
                    if (File.Exists(SystemFile.DBFile))
                    {
                        XmlDocument xml = new XmlDocument();
                        xml.Load(SystemFile.DBFile);
                        XmlNode node = xml.SelectSingleNode(string.Format("InfolightDB/DataBase/{0}", dataBaseName));
                        if (node != null)
                        {
                            string connString;
                            bool encrypt = node.Attributes["Encrypt"] == null ? false : Convert.ToBoolean(node.Attributes["Encrypt"].Value);
                            if (encrypt)
                            {
                                connString = ServerConfig.LoginObject.GetDBConnection(dataBaseName);
                            }
                            else
                            {
                                connString = node.Attributes["String"].InnerText;
                                if (connString.Length > 0)
                                {
                                    connString = connString.TrimEnd(';');
                                    if (node.Attributes["Password"].InnerText != String.Empty)
                                        connString = connString + ";Password=" + GetPwdString(node.Attributes["Password"].InnerText);
                                }
                            }

                            int maxCount = int.Parse(node.Attributes["MaxCount"].InnerText);
                            int timeOut = int.Parse(node.Attributes["TimeOut"].InnerText);
                            ClientType dbType = (ClientType)int.Parse(node.Attributes["Type"].InnerText);
                            OdbcDBType odbcType = node.Attributes["OdbcType"] != null ? (OdbcDBType)int.Parse(node.Attributes["OdbcType"].InnerText) : OdbcDBType.None;
                            DbConnection dbconn = new DbConnection(dataBaseName, connString, maxCount, timeOut, dbType);
                            dbconn.OdbcType = odbcType;
                            DbConnections.Add(dataBaseName, dbconn);
                        }


                    }
                }
                return (DbConnection)DbConnections[dataBaseName];
            }
        }

        #region DbConnection
        public class DbConnection
        {
            private int m_semaphore;
            private string m_connString;
            private string m_dbName;
            private int m_maxCount;
            private int m_timeOut;
            private ArrayList m_connectionInfoList;
            private ClientType m_dbType;
            private System.Threading.Semaphore FSemaphore = null;
            private System.Threading.Mutex FMutex;

            private IDbConnection GetReleasedConnection(string user, string module, DateTime time)
            {
                FMutex.WaitOne();
                try
                {
                    foreach (ConnectionInfo Info in ConnectionInfoList)
                    {
                        if (Info.IsReleased)
                        {
                            Info.IsReleased = false;
                            Info.SetInfo(user, module, time);
                            return Info.Connection;
                        }
                    }
                    return null;
                }
                finally
                {
                    FMutex.ReleaseMutex();
                }
            }

            public IDbConnection WaitOne(string user, string module, DateTime time)
            {
                if (FSemaphore.WaitOne(TimeOut * 1000, true))
                {
                    IDbConnection conn = null;
                    lock (this)
                    {
                        conn = GetReleasedConnection(user, module, time);
                    }
                    if (conn == null)
                    {
                        conn = CreateConnection();
                        if (conn != null)
                        {
                            lock (this)
                            {
                                ConnectionInfo info = new ConnectionInfo(conn, false);
                                info.SetInfo(user, module, time);
                                ConnectionInfoList.Add(info);
                            }
                        }
                    }
                    if (conn != null && conn.State != ConnectionState.Open)
                    {
                        conn.Open();
                    }
                    return conn;
                }
                else
                {
                    return null;
                }
            }

            public IDbConnection CreateConnection()
            {
                IDbConnection conn = null;
                if (ClientType.ctMsSql == DbType)
                {
                    conn = new SqlConnection(ConnectionString);
                }
                else if (ClientType.ctOleDB == DbType)
                {
                    conn = new OleDbConnection(ConnectionString);
                }
                else if (ClientType.ctOracle == DbType)
                {
                    conn = new OracleConnection(ConnectionString);
                    if (conn.State == ConnectionState.Closed) conn.Open();
                    OracleTransaction tran = conn.BeginTransaction(IsolationLevel.Unspecified) as OracleTransaction;
                    tran.Commit();
                    conn.Close();
                }
                else if (ClientType.ctODBC == DbType)
                {
                    conn = new OdbcConnection(ConnectionString);
                }
#if MySql
                else if (ClientType.ctMySql == DbType)
                {
                    conn = new MySql.Data.MySqlClient.MySqlConnection(ConnectionString);
                }
#endif
#if Informix
                else if (ClientType.ctInformix == DbType)
                {
                    conn = new IBM.Data.Informix.IfxConnection(ConnectionString);
                }
#endif
                else
                {
                    throw new EEPException(EEPException.ExceptionType.MethodNotSupported, this.GetType(), null, "CreateConnection", null);
                }
                return conn;
            }

            public void Release(IDbConnection Conn)
            {
                lock (this)
                {
                    foreach (ConnectionInfo Info in ConnectionInfoList)
                    {
                        if (Info.Connection == Conn && !Info.IsReleased)
                        {
                            if (Info.Connection.State == ConnectionState.Open)
                                Info.Connection.Close();
                            Info.IsReleased = true;
                            FSemaphore.Release(1);
                        }
                    }
                }
            }

            public ConnectionInfo[] GetUnReleasedConnections()
            { 
                lock(this)
                {
                    List<ConnectionInfo> infos = new List<ConnectionInfo>();
                    foreach (ConnectionInfo info in ConnectionInfoList)
                    {
                        if (!info.IsReleased)
                        {
                            infos.Add(info);
                        }
                    }

                    return infos.ToArray();
                }
            }

            public void Clear()
            {
                lock (this)
                {
                    ConnectionInfoList.Clear();
                }
            }

            public DbConnection(string dbName, string connString, int maxCount, int timeOut, ClientType dbType)
            {
                this.m_connString = connString;
                this.m_semaphore = maxCount;
                this.m_dbName = dbName;
                this.m_maxCount = maxCount;
                this.m_timeOut = timeOut;
                this.m_dbType = dbType;

                this.m_connectionInfoList = new ArrayList();
                FSemaphore = new System.Threading.Semaphore(maxCount, maxCount);
                FMutex = new System.Threading.Mutex();
            }

            public string ConnectionString
            {
                get
                {
                    return m_connString;
                }
                set
                {
                    m_connString = value;
                }
            }

            public int Semaphore
            {
                get
                {
                    return m_semaphore;
                }
                set
                {
                    m_semaphore = value;
                }
            }

            public string DbName
            {
                get
                {
                    return this.m_dbName;
                }
                set
                {
                    m_dbName = value;
                }
            }

            public int MaxCount
            {
                get
                {
                    return this.m_maxCount;
                }
                set
                {
                    this.m_maxCount = value;
                }
            }

            public int TimeOut
            {
                get
                {
                    return this.m_timeOut;
                }
                set
                {
                    this.m_timeOut = value;
                }
            }

            public ArrayList ConnectionInfoList
            {
                get
                {
                    return this.m_connectionInfoList;
                }
                set
                {
                    this.m_connectionInfoList = value;
                }
            }

            public ClientType DbType
            {
                get
                {
                    return m_dbType;
                }
                set
                {
                    m_dbType = value;
                }
            }

            private OdbcDBType odbcType = OdbcDBType.None;

            public OdbcDBType OdbcType
            {
                get { return odbcType; }
                set { odbcType = value; }
            }
        }
        #endregion DbConnection

        #region ConnectionInfo
        public class ConnectionInfo
        {
            public ConnectionInfo(IDbConnection connection, bool isReleased)
            {
                m_isReleased = isReleased;
                m_connection = connection;
            }

            private IDbConnection m_connection;
            public IDbConnection Connection
            {
                get
                {
                    return m_connection;
                }
                set
                {
                    m_connection = value;
                }
            }

            private bool m_isReleased;
            public bool IsReleased
            {
                get
                {
                    return m_isReleased;
                }
                set
                {
                    m_isReleased = value;
                }
            }

            private string userID;

            public string UserID
            {
                get { return userID; }
            }

            private string module;

            public string Module
            {
                get { return module; }
            }

            private DateTime time;

            public DateTime Time
            {
                get { return time; }
            }

            public void SetInfo(string u, string m, DateTime t)
            {
                userID = u;
                module = m;
                time = t;
            }

            public override string ToString()
            {
                return string.Format("{0}({1:yyyy/MM/dd HH:mm:ss})", UserID, Time);
            }
        }
        #endregion ConnectionInfo
    }
}
